package com.example.ole.taskqueue.Q.base;

import android.os.SystemClock;

import com.example.ole.taskqueue.Print;

import java.util.List;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.example.ole.taskqueue.Q.base.XTaskAbs.RET_NORMAL;
import static com.example.ole.taskqueue.Q.base.XTaskAbs.RET_PAUSED;

// Test result.
// D/XTaskQueue: Starting.
// D/PrioritizedRecordingQueue: Add {No.0001 Type:0}. (Normal: 1 Event: 0)
// D/PrioritizedRecordingQueue: Add {No.0002 Type:0}. (Normal: 2 Event: 0)
// D/PrioritizedRecordingQueue: Rmv {No.0001 Type:0}. (Normal: 1 Event: 0)
// D/RecTask-0001-0: Starting Recording.
// D/RecTask-0001-0: stage: 0
// D/RecTask-0001-0: stage: 1
// D/RecordingProgressCtrl: Pausing normal recording
// D/PrioritizedRecordingQueue: Add {No.0003 Type:1}. (Normal: 1 Event: 1)
// D/PrioritizedRecordingQueue: Add {No.0004 Type:2}. (Normal: 1 Event: 2)
// D/PrioritizedRecordingQueue: Add {No.0005 Type:0}. (Normal: 2 Event: 2)
// D/RecTask-0001-0: stage: 2
// D/RecTask-0001-0: Stopped Recording returns 1-PAUSED.
// D/RecTask-0001-0: Requeue {No.0001 Type:0} to task queue.
// D/PrioritizedRecordingQueue: Add {No.0001 Type:0}. (Normal: 3 Event: 2)
// D/PrioritizedRecordingQueue: Rmv {No.0004 Type:2}. (Normal: 3 Event: 1)
// D/RecTask-0004-2: Starting Recording.
// D/RecTask-0004-2: stage: 0
// D/RecTask-0004-2: stage: 1
// D/RecTask-0004-2: stage: 2
// D/RecTask-0004-2: Stopped Recording returns 0-NORMAL.
// D/PrioritizedRecordingQueue: Rmv {No.0003 Type:1}. (Normal: 3 Event: 0)
// D/RecTask-0003-1: Starting Recording.
// D/RecTask-0003-1: stage: 0
// D/RecTask-0003-1: stage: 1
// D/RecTask-0003-1: stage: 2
// D/RecTask-0003-1: Stopped Recording returns 0-NORMAL.
// D/RecordingProgressCtrl: Resuming normal recording
// D/PrioritizedRecordingQueue: Rmv {No.0001 Type:0}. (Normal: 2 Event: 0)
// D/RecTask-0001-0: Starting Recording.
// D/RecTask-0001-0: stage: 3
// D/RecTask-0001-0: stage: 1
// D/RecTask-0001-0: stage: 2
// D/RecTask-0001-0: Stopped Recording returns 0-NORMAL.
// D/PrioritizedRecordingQueue: Rmv {No.0002 Type:0}. (Normal: 1 Event: 0)
// D/RecTask-0002-0: Starting Recording.
// D/RecTask-0002-0: stage: 0
// D/RecTask-0002-0: stage: 1
// D/RecTask-0002-0: stage: 2
// D/RecTask-0002-0: Stopped Recording returns 0-NORMAL.
// D/PrioritizedRecordingQueue: Rmv {No.0005 Type:0}. (Normal: 0 Event: 0)
// D/RecTask-0005-0: Starting Recording.
// D/RecTask-0005-0: stage: 0
// D/RecTask-0005-0: stage: 1
// D/RecTask-0005-0: stage: 2
// D/RecTask-0005-0: Stopped Recording returns 0-NORMAL.
// D/XTaskQueue: Stopping.

public class XExecutor extends ThreadPoolExecutor {
    private final String TAG = "XExecutor";
    private final XProgressController mNormalProgressCtrl;
    private final XProgressController mEventProgressCtrl;
    private final PrioritizedRecordingQueue<XTask> mTaskQueue;

    public XExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, PrioritizedRecordingQueue<XTask> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        mTaskQueue = workQueue;
        mNormalProgressCtrl = new XProgressController();
        mEventProgressCtrl = new XProgressController();
    }

    @Override
    public void execute(Runnable command) {
        XTask rt = (XTask) command;

        if (rt.getEventType() != XEvent.NORMAL) {
            rt.setProgressController(mEventProgressCtrl);
            pauseNormal();
        } else {
            rt.setProgressController(mNormalProgressCtrl);
        }

        super.execute(command);
    }

    @Override
    public void shutdown() {
        super.shutdown();
        terminateRecordingAsync();
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> dropped = super.shutdownNow();
        terminateRecordingAsync();
        return dropped;
    }

    private void pauseNormal() {
        mNormalProgressCtrl.pause();
    }

    private void resumeNormal() {
        mNormalProgressCtrl.resume();
    }

    //For shutdown() There are no guarantees beyond best-effort attempts to stop processing actively executing tasks.
    //So we use the progress controller to break the executing tasks.
    private void terminateRecordingAsync() {
        mNormalProgressCtrl.terminate();
        mEventProgressCtrl.terminate();
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        XTaskAbs rt = (XTaskAbs) r;
        Print.d(1, 1, rt.TAG, (rt.getTaskResult() == RET_NORMAL ? "Starting Recording." : "Resuming Recording."));
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);

        XTaskAbs rt = (XTaskAbs) r;
        Print.d(1, 1, rt.TAG, (rt.getTaskResult() == RET_NORMAL ? "Stopped Recording." : rt.getTaskResult() == RET_PAUSED ? "Paused Recording." : "Terminated Recording."));

        if (rt.getEventType() != XEvent.NORMAL) {
            maybeResumeNormal();
        }

        if (rt.getTaskResult() == RET_PAUSED) {
            Print.d(1, 1, rt.TAG, "Requeue " + rt.identity() + " to task queue.");
            SystemClock.sleep(1000);
            super.execute(rt);
        }
    }

    private void maybeResumeNormal() {
        if (mTaskQueue.getEventTaskCount() == 0) {
            resumeNormal();
        }
    }
}
